﻿
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;

namespace CNative
{
    public static partial class Utils
    {
        /// <summary>
        /// 把一个字符串类型转化为可能的俘点型
        /// </summary>
        /// <param name="S"></param>
        /// <returns></returns>
        public static decimal ToDecimal(string S)
        { return Utilities.FuncStr.ToDecimal(S); }
        /// <summary>
        /// 判断字符串是否包含非数字字符
        /// </summary>
        /// <returns>返回是否</returns>
        public static bool IsNumber(string str)
        {
            return Utilities.FuncStr.IsNumber(str);
        }

        /// <summary>
        /// 转为字节数组
        /// </summary>
        /// <param name="base64Str">base64字符串</param>
        /// <returns></returns>
        public static byte[] ToBytes_FromBase64Str(this string base64Str)
        {
            return Convert.FromBase64String(base64Str);
        }

        /// <summary>
        /// 转换为MD5加密后的字符串（默认加密为32位）
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string ToMD5String(this string str)
        {
            MD5 md5 = MD5.Create();
            byte[] inputBytes = Encoding.UTF8.GetBytes(str);
            byte[] hashBytes = md5.ComputeHash(inputBytes);

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < hashBytes.Length; i++)
            {
                sb.Append(hashBytes[i].ToString("x2"));
            }
            md5.Dispose();

            return sb.ToString();
        }
        /// <summary>
        /// Base64加密
        /// 注:默认采用UTF8编码
        /// </summary>
        /// <param name="source">待加密的明文</param>
        /// <returns>加密后的字符串</returns>
        public static string Base64Encode(this string source)
        {
            return Base64Encode(source, Encoding.UTF8);
        }

        /// <summary>
        /// Base64加密
        /// </summary>
        /// <param name="source">待加密的明文</param>
        /// <param name="encoding">加密采用的编码方式</param>
        /// <returns></returns>
        public static string Base64Encode(this string source, Encoding encoding)
        {
            string encode = string.Empty;
            byte[] bytes = encoding.GetBytes(source);
            try
            {
                encode = Convert.ToBase64String(bytes);
            }
            catch
            {
                encode = source;
            }
            return encode;
        }

        /// <summary>
        /// Base64解密
        /// 注:默认使用UTF8编码
        /// </summary>
        /// <param name="result">待解密的密文</param>
        /// <returns>解密后的字符串</returns>
        public static string Base64Decode(this string result)
        {
            return Base64Decode(result, Encoding.UTF8);
        }

        /// <summary>
        /// Base64解密
        /// </summary>
        /// <param name="result">待解密的密文</param>
        /// <param name="encoding">解密采用的编码方式，注意和加密时采用的方式一致</param>
        /// <returns>解密后的字符串</returns>
        public static string Base64Decode(this string result, Encoding encoding)
        {
            string decode = string.Empty;
            byte[] bytes = Convert.FromBase64String(result);
            try
            {
                decode = encoding.GetString(bytes);
            }
            catch
            {
                decode = result;
            }
            return decode;
        }

        /// <summary>
        /// 计算SHA1摘要
        /// 注：默认使用UTF8编码
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static byte[] ToSHA1Bytes(this string str)
        {
            return str.ToSHA1Bytes(Encoding.UTF8);
        }

        /// <summary>
        /// 计算SHA1摘要
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="encoding">编码</param>
        /// <returns></returns>
        public static byte[] ToSHA1Bytes(this string str, Encoding encoding)
        {
            SHA1 sha1 = new SHA1CryptoServiceProvider();
            byte[] inputBytes = encoding.GetBytes(str);
            byte[] outputBytes = sha1.ComputeHash(inputBytes);

            return outputBytes;
        }

        /// <summary>
        /// 转为SHA1哈希加密字符串
        /// 注：默认使用UTF8编码
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static string ToSHA1String(this string str)
        {
            return str.ToSHA1String(Encoding.UTF8);
        }

        /// <summary>
        /// 转为SHA1哈希
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="encoding">编码</param>
        /// <returns></returns>
        public static string ToSHA1String(this string str, Encoding encoding)
        {
            byte[] sha1Bytes = str.ToSHA1Bytes(encoding);
            string resStr = BitConverter.ToString(sha1Bytes);
            return resStr.Replace("-", "").ToLower();
        }

        /// <summary>
        /// string转int
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static int ToInt(this string str)
        {
            str = str.Replace("\0", "");
            if (string.IsNullOrEmpty(str))
                return 0;
            return Convert.ToInt32(str);
        }

        /// <summary>
        /// 二进制字符串转为Int
        /// </summary>
        /// <param name="str">二进制字符串</param>
        /// <returns></returns>
        public static int ToInt_FromBinString(this string str)
        {
            return Convert.ToInt32(str, 2);
        }

        /// <summary>
        /// 将16进制字符串转为Int
        /// </summary>
        /// <param name="str">数值</param>
        /// <returns></returns>
        public static int ToInt0X(this string str)
        {
            int num = Int32.Parse(str, NumberStyles.HexNumber);
            return num;
        }

        /// <summary>
        /// 转换为double
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static double ToDouble(this string str)
        {
            return Convert.ToDouble(str);
        }

        /// <summary>
        /// string转byte[]
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static byte[] ToBytes(this string str)
        {
            return Encoding.Default.GetBytes(str);
        }

        /// <summary>
        /// string转byte[]
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="theEncoding">需要的编码</param>
        /// <returns></returns>
        public static byte[] ToBytes(this string str, Encoding theEncoding)
        {
            return theEncoding.GetBytes(str);
        }

        /// <summary>
        /// 将16进制字符串转为Byte数组
        /// </summary>
        /// <param name="str">16进制字符串(2个16进制字符表示一个Byte)</param>
        /// <returns></returns>
        public static byte[] To0XBytes(this string str)
        {
            List<byte> resBytes = new List<byte>();
            for (int i = 0; i < str.Length; i = i + 2)
            {
                string numStr = $@"{str[i]}{str[i + 1]}";
                resBytes.Add((byte)numStr.ToInt0X());
            }

            return resBytes.ToArray();
        }

        /// <summary>
        /// 将ASCII码形式的字符串转为对应字节数组
        /// 注：一个字节一个ASCII码字符
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static byte[] ToASCIIBytes(this string str)
        {
            return str.ToList().Select(x => (byte)x).ToArray();
        }

        /// <summary>
        /// 转换为日期格式
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static DateTime ToDateTime(this string str)
        {
            return Convert.ToDateTime(str);
        }

        /// <summary>
        /// 转换为日期格式
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static DateTime? ToDateTimeNull(this string str)
        {
            if (str.IsNullOrEmpty())
            {
                return null;
            }
            return Convert.ToDateTime(str);
        }


        /// <summary>
        /// 删除Json字符串中键中的@符号
        /// </summary>
        /// <param name="jsonStr">json字符串</param>
        /// <returns></returns>
        public static string RemoveAt(this string jsonStr)
        {
            Regex reg = new Regex("\"@([^ \"]*)\"\\s*:\\s*\"(([^ \"]+\\s*)*)\"");
            string strPatten = "\"$1\":\"$2\"";
            return reg.Replace(jsonStr, strPatten);
        }


        /// <summary>
        /// 转为首字母大写
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static string ToFirstUpperStr(this string str)
        {
            return str.Substring(0, 1).ToUpper() + str.Substring(1);
        }

        /// <summary>
        /// 转为首字母小写
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static string ToFirstLowerStr(this string str)
        {
            return str.Substring(0, 1).ToLower() + str.Substring(1);
        }

        /// <summary>
        /// 转为网络终结点IPEndPoint
        /// </summary>=
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static IPEndPoint ToIPEndPoint(this string str)
        {
            IPEndPoint iPEndPoint = null;
            try
            {
                string[] strArray = str.Split(':').ToArray();
                string addr = strArray[0];
                int port = Convert.ToInt32(strArray[1]);
                iPEndPoint = new IPEndPoint(IPAddress.Parse(addr), port);
            }
            catch
            {
                iPEndPoint = null;
            }

            return iPEndPoint;
        }

        /// <summary>
        ///   将一个或多个枚举常数的名称或数字值的字符串表示转换成等效的枚举对象。
        /// </summary>
        /// <param name="value">要转换的枚举名称或基础值的字符串表示形式。</param>
        /// <typeparam name="TEnum">
        ///   要将 <paramref name="value" /> 转换到的枚举类型。
        /// </typeparam>
        /// <returns>
        ///   当此方法返回时，如果分析操作成功，<paramref name="value" /> 将包含值由 <paramref name="value" /> 表示的 <paramref name="TEnum" /> 类型的对象。
        ///    如果分析操作失败，<paramref name="value" /> 包括 <paramref name="TEnum" /> 的基础类型的默认值。
        ///    请注意，此值无需为 <paramref name="TEnum" /> 枚举的成员。
        ///    此参数未经初始化即被传递。
        /// </returns>
        public static TEnum ToEnum<TEnum>(this string value) where TEnum : struct
        {
            if (Enum.TryParse<TEnum>(value, out TEnum enumStr))
            {
                return enumStr;
            }

            return default(TEnum);
        }

        /// <summary>
        /// 将汉字字符串hzs转化为拼音首拼，如果中间有非汉字则直接输出
        /// </summary>
        /// <param name="hzs"></param>
        /// <returns></returns>
        public static String ToHzPY(this String hzs)
        {
            return Utilities.FuncCode.GetHzPY(hzs);
        } 
        /// <summary>
          /// 将汉字字符串hzs转化为五笔首字母，如果中间有非汉字则直接输出
          /// </summary>
          /// <param name="hzs"></param>
          /// <returns></returns>
        public static String ToHzWB(this String hzs)
        {
            return Utilities.FuncCode.GetHzWB(hzs);
        }
        /// <summary>
        /// 将汉字字符串hzs转化为拼音首拼+五笔首字母，如果中间有非汉字则直接输出
        /// </summary>
        /// <param name="hzs"></param>
        /// <returns>py+"|"+wb</returns>
        public static String ToHzPYAndWB(this String hzs)
        {
            return Utilities.FuncCode.GetHzPY(hzs) + "|" + Utilities.FuncCode.GetHzWB(hzs);
        }

        /// <summary>
        /// 判断当前字符串实际长度是否大于最大长度
        /// </summary>
        /// <param name="str"></param>
        /// <param name="Maxlen"></param>
        /// <returns>大于Maxlen:true;小于等于Maxlen:false</returns>
        public static bool CheckInputLenth(this string str, int Maxlen)
        {
            if (System.Text.Encoding.Default.GetByteCount(str) > Maxlen)
            {
                return true;
            }
            return false;
        }

        public static bool IsIP(this string input) => input.IsMatch(@"\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\:\d{2,5}\b");

        public static bool IsMatch(this string str, string op)
        {
            if (str.Equals(String.Empty) || str == null) return false;
            var re = new Regex(op, RegexOptions.IgnoreCase);
            return re.IsMatch(str);
        }

        public static string[] Split(this string source, string sp)
        {
            if (source.IsNullOrEmpty()) return new string[0];
            var chars = sp.ToCharArray();
            return source.Split(chars);
        }

        public static StringBuilder AppendJoin(this StringBuilder source, String separator, params object[] values)
        {
            if (source.IsNullOrEmpty()) return new StringBuilder();
            return source.Append(string.Join(separator, values));
        }
        public static StringBuilder AppendJoin(this StringBuilder source, String separator, params String[] value)
        {
            if (source.IsNullOrEmpty()) return new StringBuilder();
            return source.Append(string.Join(separator, value));
        }
        public static StringBuilder AppendJoin(this StringBuilder source, String separator, IEnumerable<String> values)
        {
            if (source.IsNullOrEmpty()) return new StringBuilder();
            return source.Append(string.Join(separator, values));
        }
        public static StringBuilder AppendJoin(this StringBuilder source, char separator, params object[] values)
        {
            if (source.IsNullOrEmpty()) return new StringBuilder();
            return source.Append(string.Join("" + separator, values));
        }
        public static StringBuilder AppendJoin(this StringBuilder source, char separator, params String[] value)
        {
            if (source.IsNullOrEmpty()) return new StringBuilder();
            return source.Append(string.Join("" + separator, value));
        }
        public static StringBuilder AppendJoin(this StringBuilder source, char separator, IEnumerable<String> values)
        {
            if (source.IsNullOrEmpty()) return new StringBuilder();
            return source.Append(string.Join(""+separator, values));
        }
        public static string Join(this string source, char separator, params object[] values)
        {
            if (source.IsNullOrEmpty()) return "";
            return string.Join("" + separator, values);
        }
        public static string Join(this string source, char separator, params string[] value)
        {
            if (source.IsNullOrEmpty()) return "";
            return  string.Join("" + separator, value);
        }
        public static string Join(this string source, char separator, IEnumerable<string> values)
        {
            if (source.IsNullOrEmpty()) return "";
            return string.Join("" + separator, values);
        }

        /// <summary>
        /// Default IgnoreCase StringComparison
        /// </summary>
        public static StringComparison CurrentIgnoreCaseStringComparison = StringComparison.OrdinalIgnoreCase;


        /// <summary>
        /// return 'defaultValue' if 'value' is [null | string.Empty], elsewhere 'value' is returned
        /// </summary>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static string WithDefault(this string value, string defaultValue)
        {
            return value.IsNullOrEmpty() ? defaultValue : value;
        }

        /// <summary>
        /// return 'defaultValue' if 'value' is [null | string.Empty], elsewhere 'value' is returned
        /// </summary>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static string WithDefault(this string value, bool defaultValue)
        {
            return value.IsNullOrEmpty() ? defaultValue.ToString().ToLower() : value;
        }

        /// <summary>
        /// return true if value is [true] - Case is Ignored
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsTrue(this string value)
        {
            return (value.IsNullOrEmpty() ? false.ToString().ToLower() : value)
                .EqualsIgnoreCase(true.ToString());
        }

        /// <summary>
        /// return true if value is [null | string.Empty | false] - Case is Ignored
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsFalse(this string value)
        {
            return (value.IsNullOrEmpty() ? false.ToString().ToLower() : value)
                .EqualsIgnoreCase(false.ToString());
        }


        ///// <summary>
        ///// return true if value is [null | string.Empty]
        ///// </summary>
        ///// <param name="value"></param>
        ///// <returns></returns>
        //public static bool IsEmpty(this string value)
        //{
        //    return String.IsNullOrEmpty(value);
        //}

        ///// <summary>
        ///// return true if value is NOT [null | string.Empty]
        ///// </summary>
        ///// <param name="value"></param>
        ///// <returns></returns>
        //public static bool IsNotEmpty(this string value)
        //{
        //    return !value.IsNullOrEmpty();
        //}


        /// <summary>
        /// Contains(searchValue) with StringComparison=<see cref="CurrentIgnoreCaseStringComparison"/>
        /// </summary>
        /// <param name="value"></param>
        /// <param name="searchValue"></param>
        /// <returns></returns>
        public static bool ContainsIgnoreCase(this string value, string searchValue)
        {
            return value.Contains(searchValue, CurrentIgnoreCaseStringComparison);
        }

        /// <summary>
        /// EndsWith(searchValue) with StringComparison=<see cref="CurrentIgnoreCaseStringComparison"/>
        /// </summary>
        /// <param name="value"></param>
        /// <param name="searchValue"></param>
        /// <returns></returns>
        public static bool EndsWithIgnoreCase(this string value, string searchValue)
        {
            return value.EndsWith(searchValue, CurrentIgnoreCaseStringComparison);
        }

        /// <summary>
        /// EndsWith(searchValue) with StringComparison=<see cref="CurrentIgnoreCaseStringComparison"/>
        /// </summary>
        /// <param name="value"></param>
        /// <param name="searchValue"></param>
        /// <returns></returns>
        public static bool StartsWithIgnoreCase(this string value, string searchValue)
        {
            return value.StartsWith(searchValue, CurrentIgnoreCaseStringComparison);
        }


        /// <summary>
        /// Equals(searchValue) with tringComparison=<see cref="CurrentIgnoreCaseStringComparison"/>
        /// </summary>
        /// <param name="value"></param>
        /// <param name="searchValue"></param>
        /// <returns></returns>
        public static bool EqualsIgnoreCase(
            this string value,
            string searchValue)
        {
            return value?.Equals(searchValue, CurrentIgnoreCaseStringComparison) ?? false;
        }

        /// <summary>
        /// Contains(searchValue) with StringComparison
        /// </summary>
        /// <param name="value"></param>
        /// <param name="searchValue"></param>
        /// <param name="stringComparison"></param>
        /// <returns></returns>
        public static bool Contains(
            this string value,
            string searchValue,
            StringComparison stringComparison)
        {
            return (value?.IndexOf(searchValue, stringComparison) ?? -1) > -1;
        }

        /// <summary>
        /// Convert 'value' to int?
        /// </summary>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static int? TryConvertToInt(this string value, int? defaultValue = null)
        {
            return Int32.TryParse(value, out var parsedValue) ? parsedValue : defaultValue;
        }

        /// <summary>
        /// Convert 'value' to long?
        /// </summary>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static long? TryConvertToLong(this string value, long? defaultValue = null)
        {
            return Int64.TryParse(value, out var parsedValue) ? parsedValue : defaultValue;
        }

        /// <summary>
        /// Convert 'value' to double?
        /// </summary>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static double? TryConvertToDouble(this string value, double? defaultValue = null)
        {
            return Double.TryParse(value, out var parsedValue) ? parsedValue : defaultValue;
        }

        /// <summary>
        /// return prefix == null ? name : $"{prefix}{name}"; 
        /// </summary>
        /// <param name="name">
        /// name of the object to add prefix to.<br/>
        /// If name start with value of {<paramref name="prefix"/>}, the value of <paramref name="name"/> will be returned
        /// </param>
        /// <param name="prefix">if <paramref name="prefix"/> is NULL, the value will be defaulted to <paramref name="defaultPrefix'"/></param>
        /// <param name="defaultPrefix"></param>
        /// <returns></returns>
        public static string GetPrefixedName(this string name, string prefix, string defaultPrefix = null)
        {
            prefix = prefix ?? defaultPrefix ?? String.Empty;

            if (prefix.IsNullOrEmpty() || name.IsNullOrEmpty())
            {
                return name;
            }

            return name.StartsWith(prefix, StringComparison.Ordinal) ? name : $"{prefix}{name}";
        }


        /// <summary>
        /// remove {prefix} if name start with {prefix}
        /// </summary>
        /// <param name="name"></param>
        /// <param name="prefix">if <paramref name="prefix"/> is NULL, the value will be defaulted to <paramref name="defaultPrefix'"/></param>
        /// <param name="defaultPrefix"></param>
        /// <returns></returns>
        public static string TrimPrefixName(this string name, string prefix, string defaultPrefix = null)
        {
            prefix = prefix ?? defaultPrefix ?? String.Empty;
            if (prefix.IsNullOrEmpty() || name.IsNullOrEmpty())
            {
                return name;
            }

            return name.StartsWith(prefix, StringComparison.Ordinal) ? name.Substring(prefix.Length) : name;
        }

        /// <summary>
        /// Returns a new string in which all occurrences of a specified string in the current instance are replaced with another specified string.<br/>
        /// This function is case-insensitive
        /// </summary>
        /// <param name="value">source string-instance</param>
        /// <param name="oldValue">The string to be replaced.</param>
        /// <param name="newValue">The string to replace all occurrences of oldValue.</param>
        /// <returns>A string that is equivalent to the current string except that all instances of <paramref name="oldValue">oldValue</paramref> are replaced with <paramref name="newValue">newValue</paramref>. If <paramref name="oldValue">oldValue</paramref> is not found in the current instance, the method returns the current instance unchanged.</returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="oldValue">oldValue</paramref> is null.</exception>
        /// <exception cref="T:System.ArgumentException"><paramref name="oldValue">oldValue</paramref> is the empty string ("").</exception>
        public static string ReplaceIgnoreCase(this string value, string oldValue, string newValue)
        {
            var pos = value.IndexOf(oldValue, CurrentIgnoreCaseStringComparison);
            if (pos == -1)
            {
                return value;
            }

            var oldValueToReplace = value.Substring(pos, oldValue.Length);

            return value.Replace(oldValueToReplace, newValue);
        }

        /// <summary>
        /// Retrieves a substring from this instance. The substring is from position 0 to the position of <paramref name="to"/> string <br/>
        /// If position of <paramref name="to"/> is less 0, the incoming string is returned.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="to"></param>
        /// <returns></returns>
        public static string SubstringTo(this string value, string to)
        {
            var pos = value.IndexOf(to, CurrentIgnoreCaseStringComparison);
            return pos > -1 ? value.Substring(0, pos) : value;
        }

        /// <summary>
        /// Retrieves a substring from this instance. The substring is from position 0 to the position of <paramref name="to"/> string <br/>
        /// If position of <paramref name="to"/> is less 0, the incoming string is returned.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <returns></returns>
        public static string SubstringFrom(this string value, string from, params string[] to)
        {
            var pos = value.IndexOf(from, CurrentIgnoreCaseStringComparison);

            if (pos <= -1)
            {
                return value;
            }

            var subStr = value.Substring(pos);
            pos = -1;
            foreach (var s in to)
            {
                var posFount = subStr.IndexOf(s, from.Length - 1, CurrentIgnoreCaseStringComparison);
                if (posFount > -1 && (pos == -1 || posFount < pos))
                {
                    pos = posFount;
                }
            }
            return pos > -1 ? subStr.Substring(0, pos).Trim() : subStr.Trim();
        }

        /// <summary>
        /// Retrieves a substring from this instance. The substring is from position 0 to the position of <paramref name="to"/> string <br/>
        /// If position of <paramref name="to"/> is less 0, the incoming string is returned.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <returns></returns>
        public static string SubstringFromAdnIncludeToString(this string value, string from, params string[] to)
        {
            var pos = value.IndexOf(from, CurrentIgnoreCaseStringComparison);

            if (pos <= -1)
            {
                return value;
            }

            var subStr = value.Substring(pos);
            pos = -1;
            var toString = string.Empty;
            foreach (var s in to)
            {
                toString = s;
                var posFount = subStr.IndexOf(s, from.Length - 1, CurrentIgnoreCaseStringComparison);
                if (posFount > -1 && (pos == -1 || posFount < pos))
                {
                    pos = posFount;
                }
            }
            return pos > -1 ? $"{subStr.Substring(0, pos).Trim()}{toString}" : subStr.Trim();
        }

        /// <summary>
        ///   [InlineData("   20151231234500.1234+23581")]
        ///  [InlineData("20151231234500.1234+23")]
        ///   [InlineData("20151231234500.12345")]
        ///  [InlineData("20151231234500.")]
        ///   [InlineData("2015123123450")]
        ///  [InlineData("20151231234")]
        ///   [InlineData("201512312")]
        /// </summary>
        /// <param name="dateTimeString"></param>
        /// <param name="throwExeption"></param>
        /// <returns></returns>
        public static DateTime? ParseDateTime(this string dateTimeString, bool throwExeption = false)
        {
            return ParseDateTime(dateTimeString, out TimeSpan offset, throwExeption);
        }
        /// <summary>
        ///   [InlineData("   20151231234500.1234+23581")]
        ///  [InlineData("20151231234500.1234+23")]
        ///   [InlineData("20151231234500.12345")]
        ///  [InlineData("20151231234500.")]
        ///   [InlineData("2015123123450")]
        ///  [InlineData("20151231234")]
        ///   [InlineData("201512312")]
        /// </summary>
        /// <param name="dateTimeString"></param>
        /// <param name="offset"></param>
        /// <param name="throwExeption"></param>
        /// <returns></returns>
        public static DateTime? ParseDateTime(this string dateTimeString, out TimeSpan offset, bool throwExeption = false)
        {
            var expr = @"^\s*((?:19|20)[0-9]{2})(?:(1[0-2]|0[1-9])(?:(3[0-1]|[1-2][0-9]|0[1-9])(?:([0-1][0-9]|2[0-3])(?:([0-5][0-9])(?:([0-5][0-9](?:\.[0-9]{1,4})?)?)?)?)?)?)?(?:([+-][0-1][0-9]|[+-]2[0-3])([0-5][0-9]))?\s*$";
            var matches = Regex.Matches(dateTimeString, expr, RegexOptions.Singleline);
            offset = TimeSpan.Zero;
            try
            {
                if (matches.Count != 1)
                    throw new FormatException("Invalid date format");

                var groups = matches[0].Groups;
                int year = int.Parse(groups[1].Value);
                int month = groups[2].Success ? int.Parse(groups[2].Value, CultureInfo.InvariantCulture) : 1;
                int day = groups[3].Success ? int.Parse(groups[3].Value, CultureInfo.InvariantCulture) : 1;
                int hours = groups[4].Success ? int.Parse(groups[4].Value, CultureInfo.InvariantCulture) : 0;
                int mins = groups[5].Success ? int.Parse(groups[5].Value, CultureInfo.InvariantCulture) : 0;

                float fsecs = groups[6].Success ? float.Parse(groups[6].Value, CultureInfo.InvariantCulture) : 0;
                int secs = (int)Math.Truncate(fsecs);
                int msecs = (int)Math.Truncate(fsecs * 1000) % 1000;

                int tzh = groups[7].Success ? int.Parse(groups[7].Value, CultureInfo.InvariantCulture) : 0;
                int tzm = groups[8].Success ? int.Parse(groups[8].Value, CultureInfo.InvariantCulture) : 0;
                offset = new TimeSpan(tzh, tzm, 0);

                return new DateTime(year, month, day, hours, mins, secs, msecs);
            }
            catch
            {
                if (throwExeption)
                    throw;

                return null;
            }
        }
    }
}
